#pragma once

#include <iostream>
#include "log.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <string.h>
#include <unordered_map>

#define SIZE 1024

using func_t = std::function<std::string(const std::string &)>;

Log log;

enum{
    SOCKET_ERR=1,
    INADDR_ERR,
    BIND_ERR,
    PORT_ERR,
};

const uint16_t defaultport = 8080;
const std::string defaultip = "0.0.0.0";

class UDPServer{
private:
    void CheckUser(struct sockaddr_in &client, const uint16_t clientport, const std::string &clientip){
        auto it = online_user.find(clientip);
        if(it == online_user.end()){
            // 用户不存在，添加到哈希表中 
            online_user.insert({clientip, client});
            std::cout << "[" << clientip << ":" << clientport << "] add to online user." << std::endl;
        }
    }

    void BroadCast(const std::string &info, const uint16_t clientport, const std::string &clientip){
        // 信息处理
        std::string message = "[";
        message += clientip;
        message += ":";
        message += std::to_string(clientport);
        message += "]# ";
        message += info;

        std::cout << "server get a message: " << message << std::endl;

        // 依次编译哈希表 将信息发送给每一个用户
        for(const auto &user : online_user){
            socklen_t len = sizeof(user.second);
            sendto(_sockfd, message.c_str(), message.size(), 0, (const struct sockaddr *)(&user.second), len);
        }
    }

public:
    UDPServer(const uint16_t port=defaultport,const std::string ip=defaultip)
    :_sockfd(0),_port(port),_ip(ip),_isrunning(false)
    {
        // 检查端口号是否合法
        if(_port < 1024){
            log(Fatal, "Port number %d is too low, please use a port number > 1024", _port);
            exit(PORT_ERR);
        }
    } 

    void Init(){
        // 1.创建udp socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0){
            log(Fatal, "server socket create error, sockfd: %d", _sockfd);
            exit(SOCKET_ERR);
        }
        log(INFO, "server socket create success, sockfd: %d", _sockfd);

        // 2.连接udp socket
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);     // 端口号从主机字节序转换为网络字节序
        
        // 检查 IP 地址是否有效
        if (_ip == "0.0.0.0") {
            local.sin_addr.s_addr = htonl(INADDR_ANY);  // 监听所有网络接口
        } else {
            local.sin_addr.s_addr = inet_addr(_ip.c_str());
            if (local.sin_addr.s_addr == INADDR_NONE) {
                log(Fatal, "Invalid IP address: %s", _ip.c_str());
                exit(INADDR_ERR);
            }
        }

        // 将创建的socket与本地的IP地址和端口号绑定
        if(bind(_sockfd, (const struct sockaddr *)&local, sizeof(local)) < 0){
            log(Fatal, "server bind error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        log(INFO, "server bind success, errno: %d, strerror: %s", errno, strerror(errno));
    }

    void Run1(func_t fun)
    {
        _isrunning = true;
        char buffer[SIZE];
        while(_isrunning){
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // recvform的后两个参数位输出型参数
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
            if(n < 0){
                log(Warning, "server recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));
                continue;
            }
            buffer[n] = 0;
            std::string info = buffer;

            // 模拟一次数据处理
            std::string echo_string = fun(info);

            // 将处理后的数据发送到目标地址
            sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (const struct sockaddr *)&client, len);
        }
    }

    // 多用户聊天测试
    void Run2(){
        _isrunning = true;
        char buffer[SIZE];
        while(_isrunning){
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // recvform的后两个参数位输出型参数
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
            if(n < 0){
                log(Warning, "server recvfrom error, errno: %d, strerror: %s", errno, strerror(errno));
                continue;
            }
            buffer[n] = 0;

            std::string info = buffer;
            uint16_t clientport = ntohs(client.sin_port);
            std::string clientip = inet_ntoa(client.sin_addr);

            // 检查当前用户是否已经在哈希表中
            CheckUser(client, clientport, clientip);

            // 将当前信息发送给所有用户
            BroadCast(info, clientport, clientip);
        }
    }

    ~UDPServer()
    {
        if(_sockfd > 0){
            close(_sockfd);
        }
    }

private:
    int _sockfd;     // 网络文件描述符
    uint16_t _port;  // 端口号
    std::string _ip; // ip地址
    bool _isrunning;
    std::unordered_map<std::string, struct sockaddr_in> online_user;
};